/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bff.spi;

import com.inspur.edp.bff.api.dataprocessor.IChangeConvertor;
import com.inspur.edp.bff.api.dataprocessor.IDataConvertor;
import com.inspur.edp.bff.api.exception.BffFrameworkException;
import com.inspur.edp.bff.api.exception.ErrorCodes;
import com.inspur.edp.bff.api.manager.IFSManagerContext;
import com.inspur.edp.cef.entity.accessor.entity.IEntityAccessor;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.DeleteChangeDetail;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IEntityDataCollection;
import com.inspur.edp.cef.entity.i18n.MultiLanguageInfo;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;

import javax.ws.rs.NotSupportedException;
import java.util.HashMap;
import java.util.Map;

public abstract class AbstractChangeConvertor implements IChangeConvertor {

    private IFSManagerContext mgrContext;
    private String nodeCode;

    /**
     * 转换器基类构造函数。
     *
     * @param mgrContext FormServer管理类上下文接口{@link IFSManagerContext}
     */
    public AbstractChangeConvertor(IFSManagerContext mgrContext) {
        this.mgrContext = mgrContext;
    }

    public final String getNodeCode() {
        return nodeCode;
    }

    public final void setNodeCode(String value) {
        nodeCode = value;
    }

    /**
     * FormServer管理类上下文接口{@link IFSManagerContext}
     *
     * @see IFSManagerContext
     */
    protected IFSManagerContext getMgrContext() {
        return mgrContext;
    }

    /**
     * 将BE变更集数据转换成VM变更集数据
     *
     * @param beChanges BM变更集数据
     * @return 转换之后的VM变更集数据
     * @see IChangeDetail
     */
    public final IChangeDetail convertFromBEChange(IChangeDetail beChanges) {
        if (beChanges == null) {
            return null;
        }
        switch (beChanges.getChangeType()) {
            case Added:
                //TODO 待完善
                throw new BffFrameworkException(ErrorCodes.BFF_FRAMEWORK_2035);
//                break;
            case Modify:
                ModifyChangeDetail modifyChange = new ModifyChangeDetail(beChanges.getDataID());
                convertModifyPropertiesFormBE((ModifyChangeDetail) ((beChanges instanceof ModifyChangeDetail) ? beChanges : null), modifyChange);
                convertModifyChildChangesFromBE((ModifyChangeDetail) ((beChanges instanceof ModifyChangeDetail) ? beChanges : null), modifyChange);
                return modifyChange;
            case Deleted:
                //TODO
                return new DeleteChangeDetail(beChanges.getDataID());
            default:
                throw new NotSupportedException();
        }
    }

    private void convertModifyChildChangesFromBE(ModifyChangeDetail beChange, ModifyChangeDetail vmChange) {
        if (beChange.getChildChanges() == null || beChange.getChildChanges().size() == 0) {
            return;
        }
        for (Map.Entry<String, Map<String, IChangeDetail>> item : beChange.getChildChanges().entrySet()) {
            convertChildChangesFromBE(item.getKey(), item.getValue(), vmChange);
        }
    }

    private void convertChildChangesFromBE(String childCode, Map<String, IChangeDetail> beChildChanges, ModifyChangeDetail vmParentChange) {
        if (beChildChanges == null || beChildChanges.isEmpty()) {
            return;
        }

        String vmChildCode = getMgrContext().getVMCodeFromBE(childCode);
        if (StringUtils.isNullOrEmpty(vmChildCode)) {
            return;
        }
        Map<String, IChangeDetail> vmChildChanges = null;
        if (vmParentChange.getChildChanges().containsKey(vmChildCode)) {
            vmChildChanges = vmParentChange.getChildChanges().get(vmChildCode);
        } else {
            vmChildChanges = new HashMap<String, IChangeDetail>(beChildChanges.size());
            vmParentChange.getChildChanges().put(vmChildCode, vmChildChanges);
        }

        AbstractChangeConvertor childChangeConvertor = getChildChangeConvertor(vmChildCode);
        childChangeConvertor.setNodeCode(vmChildCode);
        for (IChangeDetail item : beChildChanges.values()) {
            IChangeDetail targetChildChange = childChangeConvertor.convertFromBEChange(item);
            vmChildChanges.put(targetChildChange.getDataID(), targetChildChange);
        }
    }

    /**
     * 获取子节点变更集转换器。
     *
     * @param childCode 子节点编号
     * @return 得到的子节点的属性变更转换器
     * @see AbstractChangeConvertor
     */
    protected abstract AbstractChangeConvertor getChildChangeConvertor(String childCode);

    private void convertModifyPropertiesFormBE(ModifyChangeDetail beChange, ModifyChangeDetail vmChange) {
        if (beChange.getPropertyChanges() == null || beChange.getPropertyChanges().size() == 0) {
            return;
        }

        for (Map.Entry<String, Object> item : beChange.getPropertyChanges().entrySet()) {
            convertModifyPropFromBE(item.getKey(), item.getValue(), vmChange);
        }
        for (Map.Entry<String, MultiLanguageInfo> item : beChange.getMultiLanguageInfos()
                .entrySet()) {
            String propName = item.getKey().split(MultiLanguageInfo.MULTILANGUAGETOKEN)[0];
            convertMultiLanguageModifyPropFromBE(propName, item.getValue(), vmChange);
        }
    }

    /**
     * 将BE变更属性转换成VM变更数据属性
     *
     * @param beElementCode BE节点编号
     * @param beValue       BE节点的值
     * @param vmChange      VM变更定义
     */
    protected abstract void convertModifyPropFromBE(String beElementCode, Object beValue, ModifyChangeDetail vmChange);

    protected void convertMultiLanguageModifyPropFromBE(String beElementCode, Object beValue,
                                                        ModifyChangeDetail vmChange) {

    }

    /**
     * 将VM变更集数据转换成BE变更集数据
     *
     * @param vmChanges VM变更集数据
     * @return 转换之后的BE变更集数据
     */
    public final IChangeDetail convertFromVMChange(IChangeDetail vmChanges) {
        if (vmChanges == null) {
            return null;
        }
        switch (vmChanges.getChangeType()) {
            case Added:
                AddChangeDetail addChange = (AddChangeDetail) ((vmChanges instanceof AddChangeDetail) ? vmChanges : null);
                String beCode = getMgrContext().getBECodeFromVM(getNodeCode());

                IEntityAccessor beAcsr = (IEntityAccessor) getMgrContext().getLcp().createChildData(beCode, "");

                IEntityData beData = getMgrContext().getLcp().createUnlistenableChildData(beCode, "");

                IDataConvertor convertor = getMgrContext().getDataConvertor().getChildConvertor(getNodeCode());
                convertor.convertFromVMData(addChange.getEntityData(), beData);
                beAcsr.setInnerData(beData);
                beData.getChilds().forEach((key, value) -> {
                    if (value == null || value.isEmpty()) {
                        return;
                    }
                    IEntityDataCollection childCollection = beAcsr.createAndSetChildCollection(key);
                    value.forEach(item -> {
                        IEntityAccessor childAcsr = (IEntityAccessor) getMgrContext().getLcp().createChildData(key, "");
                        childAcsr.setInnerData(item);
                        childCollection.add(childAcsr);
                    });
                });
                return new AddChangeDetail((IEntityData) beAcsr);
            case Modify:
                ModifyChangeDetail beChange = new ModifyChangeDetail(vmChanges.getDataID());
                convertModifyPropertiesFormVM(beChange, (ModifyChangeDetail) ((vmChanges instanceof ModifyChangeDetail) ? vmChanges : null));
                convertModifyChildChangesFromVM(beChange, (ModifyChangeDetail) ((vmChanges instanceof ModifyChangeDetail) ? vmChanges : null));
                return beChange;
            case Deleted:
                return new DeleteChangeDetail(vmChanges.getDataID());
            default:
                throw new NotSupportedException();
        }
    }

    private void convertModifyChildChangesFromVM(ModifyChangeDetail beChange, ModifyChangeDetail vmChange) {
        if (vmChange.getChildChanges() == null || vmChange.getChildChanges().size() == 0) {
            return;
        }

        for (Map.Entry<String, Map<String, IChangeDetail>> item : vmChange.getChildChanges().entrySet()) {
            convertChildChangesFromVM(item.getKey(), item.getValue(), beChange);
        }
    }

    private void convertChildChangesFromVM(String childCode, Map<String, IChangeDetail> vmChildChanges, ModifyChangeDetail beParent) {
        if (vmChildChanges == null || vmChildChanges.isEmpty()) {
            return;
        }


        String beChildCode = getMgrContext().getBECodeFromVM(childCode);
        if (beChildCode == null || "".equals(beChildCode))
            return;
        Map<String, IChangeDetail> beChildChanges = null;
        if (beParent.getChildChanges().containsKey(beChildCode)) {
            beChildChanges = beParent.getChildChanges().get(beChildCode);
        } else {
            beChildChanges = new HashMap<String, IChangeDetail>(vmChildChanges.size());
            beParent.getChildChanges().put(beChildCode, beChildChanges);
        }
        AbstractChangeConvertor childChangeConvertor = getChildChangeConvertor(childCode);
        if (childChangeConvertor == null)
            return;
        childChangeConvertor.setNodeCode(childCode);

        for (IChangeDetail item : vmChildChanges.values()) {
            IChangeDetail targetChildChange = childChangeConvertor.convertFromVMChange(item);
            beChildChanges.put(targetChildChange.getDataID(), targetChildChange);
        }
    }

    private void convertModifyPropertiesFormVM(ModifyChangeDetail beChange,
                                               ModifyChangeDetail vmChange) {
        if ((vmChange.getPropertyChanges() == null || vmChange.getPropertyChanges().size() == 0)
                && vmChange.getMultiLanguageInfos().isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> item : vmChange.getPropertyChanges().entrySet()) {
            convertModifyPropFromVM(item.getKey(), item.getValue(), beChange);
        }
        for (Map.Entry<String, MultiLanguageInfo> item : vmChange.getMultiLanguageInfos()
                .entrySet()) {
            String propName = item.getKey().split(MultiLanguageInfo.MULTILANGUAGETOKEN)[0];
            convertMultiLanguageModifyPropFromVo(propName, item.getValue(), beChange);
        }
    }

    /**
     * 变更集多语属性赋值，生成代码中调用
     */
    protected void setChangeDetailMultiLanguageInfo(ModifyChangeDetail changeDetail,
                                                    String voLabelId,
                                                    Object value) {
        MultiLanguageInfo targetValue = ((MultiLanguageInfo) value).clone();
        targetValue.setPropName(voLabelId + MultiLanguageInfo.MULTILANGUAGETOKEN);
        changeDetail.getMultiLanguageInfos()
                .put(voLabelId + MultiLanguageInfo.MULTILANGUAGETOKEN, targetValue);
    }

    /**
     * 将VM变更数据属性转换成BE变更数据属性
     *
     * @param vmElementCode VM字段编号
     * @param vmValue       VM变更属性值
     * @param beChange      BE变更
     * @see ModifyChangeDetail
     */
    protected abstract void convertModifyPropFromVM(String vmElementCode, Object vmValue, ModifyChangeDetail beChange);

    protected void convertMultiLanguageModifyPropFromVo(String vmElementCode, Object vmValue,
                                                        ModifyChangeDetail beChange) {

    }
}
